﻿#pragma once

#include "pch.h"
#include "Utils/D3DUtils.h"

template<typename T>
class FUploadBuffer
{
public:
    FUploadBuffer(ID3D12Device* InDevice, UINT InElementCount, bool bInIsConstantBuffer)
        : mIsConstantBuffer(bInIsConstantBuffer)
    {
        mElementByteSize = sizeof(T);

        // 常量缓冲区的大小为256B的整数倍。这是因为硬件只能按m * 256B的偏移量和n * 256B的数据
        // 长度这两种规格来查看常量数据
        // typedef struct D3D12_CONSTANT_BUFFER_VIEW_DESC {
        //  D3D12_GPU_VIRTUAL_ADDRESS BufferLocation; // 256的整数倍
        //  UINT SizeInBytes; // 256的整数倍
        // } D3D12_CONSTANT_BUFFER_VIEW_DESC;
        if (mIsConstantBuffer)
        {
            mElementByteSize = D3DUtil::CalcConstantBufferByteSize(sizeof(T));
        }

        D3D12_HEAP_PROPERTIES HeapProps(D3D12_HEAP_TYPE_UPLOAD);
        D3D12_RESOURCE_DESC UploadDesc = CD3DX12_RESOURCE_DESC::Buffer(mElementByteSize * InElementCount);
        ThrowIfFailed(InDevice->CreateCommittedResource(
            &HeapProps,
            D3D12_HEAP_FLAG_NONE,
            &UploadDesc,
            D3D12_RESOURCE_STATE_GENERIC_READ,
            nullptr,
            IID_PPV_ARGS(&mUploadBuffer)));

        ThrowIfFailed(mUploadBuffer->Map(0, nullptr, reinterpret_cast<void**>(&mMappedData)));
    }

    FUploadBuffer(const FUploadBuffer&) = delete;
    FUploadBuffer& operator=(const FUploadBuffer&) = delete;

    ~FUploadBuffer()
    {
        if (mUploadBuffer != nullptr)
        {
            mUploadBuffer->Unmap(0, nullptr);
        }

        mMappedData = nullptr;
    }

    ID3D12Resource* GetResource() const
    {
        return mUploadBuffer.Get();
    }

    void CopyData(int InElementIndex, const T& InData)
    {
        memcpy(&mMappedData[InElementIndex * mElementByteSize], &InData, sizeof(T));
    }

private:
    Microsoft::WRL::ComPtr<ID3D12Resource> mUploadBuffer;
    BYTE* mMappedData = nullptr;

    UINT mElementByteSize = 0;
    bool mIsConstantBuffer = false;
};
